use base64::Engine;
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Service {
    pub id: String,
    pub name: String,
    pub address: String,
    pub port: u16,
    pub enable_tag_override: bool,
    pub check: Option<Check>,
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Check {
    pub http: String,
    pub interval: String,
    pub timeout: String,
    pub deregister_critical_service_after: String,
}

#[derive(Clone, Debug)]
pub struct Client {
    base_url: String,
    client: reqwest::Client,
}

impl Client {
    pub fn new(host: String, port: u16) -> Self {
        Self {
            base_url: format!("http://{}:{}/v1", host, port),
            client: reqwest::Client::new(),
        }
    }

    pub fn agent(&self) -> Agent {
        Agent::new(&self)
    }

    pub fn kv(&self) -> KV {
        KV::new(&self)
    }
}
pub struct Agent {
    client: Client,
}

impl Agent {
    pub fn new(client: &Client) -> Self {
        Self {
            client: client.clone(),
        }
    }
    pub async fn register_service(
        &self,
        service: Service,
    ) -> Result<bool, Box<dyn std::error::Error>> {
        let status = self
            .client
            .client
            .put(format!("{}/agent/service/register", self.client.base_url))
            .json(&service)
            .send()
            .await?
            .status();

        Ok(status.is_success())
    }
}

pub struct KV {
    client: Client,
}

impl KV {
    pub fn new(client: &Client) -> Self {
        Self {
            client: client.clone(),
        }
    }

    pub async fn read_key(&self, key: &str) -> Result<KVPair, Box<dyn std::error::Error>> {
        let url = format!("{}/kv/{}", self.client.base_url, key);
        let kv_pairs = self
            .client
            .client
            .get(url)
            .send()
            .await?
            .json::<Vec<KVPair>>()
            .await?;

        if let Some(kv_pair) = kv_pairs.get(0) {
            return Ok(kv_pair.clone());
        } else {
            return Ok(KVPair::default());
        }
    }
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct KVPair {
    key: String,
    create_index: u64,
    modify_index: u64,
    lock_index: u64,
    flags: u64,
    value: String,
}

impl KVPair {
    pub fn value_text(&self) -> String {
        let bytes = base64::engine::general_purpose::STANDARD
            .decode(&self.value)
            .unwrap();
        String::from_utf8(bytes).unwrap()
    }
}
